package thread;

//Using a named inner class:
class InnerThread1 {
	private int countDown = 5;

	private Inner inner;

	public InnerThread1(String name) {
		inner = new Inner(name);
	}

	public void interruptThread() {
		if (inner != null)
			inner.interrupt();
	}

	private class Inner extends Thread {
		Inner(String name) {
			super(name);
			start();
		}

		@Override
		public void run() {
			while (true) {
				System.out.println(this);
				if (--countDown == 0)
					return;
				try {
					System.out.println(getName() + " begins to sleep!");
					sleep(1000);
					System.out.println(getName() + " awakes!");
				} catch (InterruptedException e) {
					throw new RuntimeException(getName()
							+ " has been interrupted!");
				}
			}
		}

		@Override
		public String toString() {
			return getName() + ": " + countDown;
		}
	}
}

// Using a anonymous class:
class InnerThread2 {
	private int countDown = 5;

	private Thread t;

	public InnerThread2(String name) {
		t = new Thread(name) {
			@Override
			public void run() {
				while (true) {
					System.out.println(this);
					if (--countDown == 0)
						return;
					try {
						System.out.println(getName() + " begins to sleep!");
						sleep(1000);
						System.out.println(getName() + " awakes!");
					} catch (InterruptedException e) {
						throw new RuntimeException(getName()
								+ " has been interrupted!");
					}
				}
			}

			@Override
			public String toString() {
				return getName() + ": " + countDown;
			}
		};
		t.start();
	}

	public void interruptThread() {
		if (t != null)
			t.interrupt();
	}
}

// Using a name Runnable implementaton:
class InnerRunnable {
	private int countDown = 5;

	private Inner inner;

	public InnerRunnable(String name) {
		inner = new Inner(name);
	}

	public void interruptThread() {
		if (inner.t != null)
			inner.t.interrupt();
	}

	private class Inner implements Runnable {
		Thread t;

		Inner(String name) {
			t = new Thread(this, name);
			t.start();
		}

		public void run() {
			while (true) {
				System.out.println(this);
				if (--countDown == 0)
					return;
				try {
					System.out.println(t.getName() + " begins to sleep!");
					Thread.sleep(1000);
					System.out.println(t.getName() + " awakes!");
				} catch (InterruptedException e) {
					throw new RuntimeException(t.getName()
							+ " has been interrupted!");
				}
			}
		}

		public String toString() {
			return t.getName() + ": " + countDown;
		}
	}
}

// A separate method to run some code as a thread:
class ThreadMethod {
	private int countDown = 5;

	private Thread t;

	private String name;

	public ThreadMethod(String name) {
		this.name = name;
	}

	public void runThread() {
		if (t == null) {
			t = new Thread(name) {
				@Override
				public void run() {
					while (true) {
						System.out.println(this);
						if (--countDown == 0)
							return;
						try {
							System.out.println(getName() + " begins to sleep!");
							Thread.sleep(1000);
							System.out.println(getName() + " awakes!");
						} catch (InterruptedException e) {
							throw new RuntimeException(getName()
									+ " has been interrupted!");
						}
					}
				}

				public String toString() {
					return getName() + ":" + countDown;
				}
			};
			t.start();
		}
	}

	public void interruptThread() {
		if (t != null)
			t.interrupt();
	}
}

public class ThreadVariations {
	
	public static void main(String[] args) throws Exception {
		InnerThread1 t1 = new InnerThread1("Inner1");
		Thread.sleep(2200);
		t1.interruptThread();

		InnerThread2 t2 = new InnerThread2("Inner2");
		Thread.sleep(2200);
		t2.interruptThread();

		InnerRunnable r1 = new InnerRunnable("InnerRunnable");
		Thread.sleep(3400);
		r1.interruptThread();
		
		ThreadMethod tm1 =new ThreadMethod("ThreadMethod");
		tm1.runThread();
		Thread.sleep(1600);
		tm1.interruptThread();
	}
}